﻿using AdminToys;
using CustomCharacter;
using CustomPlayerEffects;
using Exiled.API.Enums;
using Exiled.API.Features;
using Exiled.API.Features.Pools;
using Exiled.Events.EventArgs.Interfaces;
using Exiled.Events.EventArgs.Player;
using InventorySystem.Items.Firearms;
using MEC;
using Mirror;
using PlayerRoles;
using PlayerRoles.Ragdolls;
using PlayerStatsSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace AbilityBrawl
{
    public static class Abilities
    {
        public sealed class RandomBuff : OffensiveAbility
        {
            public RandomBuff() :
                base("RandomBuff", "获得随机buff（持续5秒）", 5, null, AbilityTrigger, null, null, null)
            {
            }

            private static AbilityOutput AbilityTrigger(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                List<EffectType> effs = Enum.GetValues(typeof(EffectType)).ToArray<EffectType>().ToList();
#pragma warning disable CS0618 // 类型或成员已过时
                effs.Remove(EffectType.Marshmallow);
#pragma warning restore CS0618 // 类型或成员已过时
                effs.Remove(EffectType.None);
                var eff = effs[UnityEngine.Random.Range(0, effs.Count)];
                player.EnableEffect(eff, 1, 5);
                return new AbilityOutput(true);
            }
        }

        public sealed class AOE : OffensiveAbility
        {
            public AOE() :
                base("AOE", "对范围敌人造成20点伤害", 5, null, AbilityTrigger, null, null, null)
            { }

            private static AbilityOutput AbilityTrigger(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                var damage = new ExplosionDamageHandler(new Footprinting.Footprint(player.ReferenceHub), Vector3.one * 5f, 20f, 1);
                foreach (var p in Player.List.Where(x => x != null && Vector3.Distance(x.Position, player.Position) <= 10f && !MethodExtensions.IsFriendly(player, x) && player != x))
                {
                    p.ReferenceHub.playerStats.DealDamage(damage);
                }
                return new AbilityOutput(true);
            }
        }

        public sealed class RealDamage : AttributeAbility
        {
            public RealDamage() :
                base("RealDamage", "所造成的伤害均为真实伤害", AttrAbility, false, new IExiledEvent[]
                {
                   EventProcessor.OnHurting
                }, null, null)
            {
            }

            private static bool AttrAbility(ref AbilityInfo info, IExiledEvent e, Player player)
            {
                if (e is HurtingEventArgs ev && ev.DamageHandler.Base is PlayerStatsSystem.AttackerDamageHandler adh && ev.Attacker == player)
                {
                    ev.IsAllowed = false;
                    ev.Player.Health -= ev.Amount;
                    if (ev.Player.Health <= 0f)
                        ev.Player.ReferenceHub.playerStats.KillPlayer(adh);
                }
                return false;
            }
        }

        public sealed class FlashTeleport : OffensiveAbility
        {
            public FlashTeleport() :
                base("FlashTeleport", "使用后60米内的玩家全体失明，消耗20HP随机传送", 60, null, AbilityFunc, Ability_Checker, null, null)
            { }

            private static bool Ability_Checker(Player instance, out string response)
            {
                if (instance.Health <= 20f)
                {
                    response = "血量低于20，无法触发";
                    return false;
                }
                response = "";
                return true;
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                player.Health -= 20f;
                foreach (Player p in Player.List.Where(x => x != player && Vector3.Distance(player.Position, x.Position) <= 20f))
                {
                    p.EnableEffect<Flashed>(1f);
                }
                Teleport(player, Map.IsLczDecontaminated);
                return new AbilityOutput(true);
            }

            private static void Teleport(Player player, bool nonLcz = false)
            {
                Room room;
                while (true)
                {
                    room = Room.List.ElementAt(UnityEngine.Random.Range(0, Room.List.Count()));
                    if (nonLcz && room.Zone == ZoneType.LightContainment)
                    {
                        continue;
                    }
                    RoomType[] BannedRoomType = { RoomType.Lcz173, RoomType.Hcz079, RoomType.Hcz106, RoomType.LczArmory, RoomType.HczArmory, RoomType.HczTesla, RoomType.Pocket, RoomType.Surface };
                    if (!BannedRoomType.Contains(room.Type))
                    {
                        break;
                    }
                }
                player.Position = room.Doors.ElementAt(0).Position + (Vector3.up * 1.5f);
            }
        }

        public sealed class FakeDeath : OffensiveAbility
        {
            public FakeDeath() : base("FakeDeath", "丢弃全部东西，装死！", 120, null, AbilityFunc, null, null, new KeyValuePair<string, object>[]
            {
                new KeyValuePair<string, object>("ragdoll", null)
            }, null)
            {
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                if (trigger == AbilityTrigger.Active)
                {
                    bool activating = info.CooldownStatus.GetStatus(player).HasFlag(CooldownStatus<Player>.CdStatus.Activating);
                    if (activating)
                    {
                        return new AbilityOutput(true, true, 0, "已主动停止装死");
                    }
                    float duration = 15f;
                    var dmg = new CustomReasonDamageHandler("他死了，但他仿佛又还活着？");
                    var doll = Ragdoll.CreateAndSpawn(new RagdollData(Server.Host.ReferenceHub, dmg, player.Role, player.Position, player.Transform.rotation, player.Nickname, 999));
                    player.EnableEffect<Invisible>(duration);
                    player.EnableEffect<Ensnared>(duration);
                    player.DropItems();
                    if (!player.IsCuffed)
                        player.Handcuff();
                    info.Datas["ragdoll"] = doll;
                    return new AbilityOutput(true, true, 15, "开始装死！可随时按下技能键取消装死");
                }
                else if (trigger == AbilityTrigger.Deactive)
                {
                    player.DisableEffect<Invisible>();
                    player.DisableEffect<Ensnared>();
                    if (player.Cuffer == null)
                        player.RemoveHandcuffs();
                    object doll = info.Datas["ragdoll"];
                    if (doll != null && doll is Ragdoll rd)
                    {
                        rd.UnSpawn();
                    }
                    info.Datas["ragdoll"] = null;
                    return new AbilityOutput(true, true, 0, "");
                }
                return new AbilityOutput(false);
            }
        }

        public sealed class GroupInvisible : OffensiveAbility
        {
            public GroupInvisible() :
                base("GroupInvisible", "使附近5m所有友方获得隐身10秒buff", 120, null, AbilityFunc, null, null, null, null)
            {
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                List<Player> players = Player.List.Where(x => MethodExtensions.IsFriendly(player, x) || player == x).ToList();
                players.ForEach(x =>
                {
                    x.EnableEffect<Invisible>(10f, true);
                    x.SendFormattedMsg("", "<color=#AAAAFF>隐身</color>", "一名友方玩家发动集体隐身技能！", 3, MsgType.Message, ScreenLocation.CenterTop, false);
                });
                return new AbilityOutput(true);
            }
        }

        public sealed class FlashAttack : PassiveAbility
        {
            public FlashAttack() :
                base("FlashAttack", "血量低于60时，受到攻击会给予攻击者10秒闪光效果，同时给予肾上腺素效果", 50, AbilityCheck, AbilityFunc, null, new IExiledEvent[]
                {
                    EventProcessor.OnHurting
                }, null, null)
            {
            }

            private static bool AbilityCheck(ref AbilityInfo info, IExiledEvent e, Player player)
            {
                if (e is HurtingEventArgs ev && ev.DamageHandler.Base is PlayerStatsSystem.AttackerDamageHandler && ev.Player == player)
                {
                    if (player.Health - ev.Amount <= 60f)
                    {
                        return true;
                    }
                }
                return false;
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, IExiledEvent e, AbilityTrigger trigger)
            {
                if (e is HurtingEventArgs ev)
                {
                    ev.Attacker.EnableEffect<Flashed>(10f);
                    ev.Player.EnableEffect<Invigorated>(10f);
                    ev.Player.AddAhp(30f);
                    return new AbilityOutput(true);
                }
                return new AbilityOutput(false);
            }
        }

        public sealed class Ghost : OffensiveAbility
        {
            public Ghost() :
                base("Ghost", "消耗1%血量，获得1秒鬼魂效果（穿门）", 20, null, AbilityFunc, AbilityCheck, null, null, null)
            {
            }

            private static bool AbilityCheck(Player instance, out string response)
            {
                float per = instance.Health / instance.MaxHealth;
                if (per < 0.5f)
                {
                    response = "血量低于50%时不可用";
                    return false;
                }
                response = "";
                return true;
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                player.EnableEffect<Ghostly>(1f, true);
                player.Health -= player.MaxHealth * 0.01f;
                return new AbilityOutput(true);
            }
        }

        public sealed class Rise : PassiveAbility
        {
            public Rise() : base("Rise", "濒死时立即会满血，并获得心脏骤停效果，隐身3秒", 60, AbilityCheck, AbilityFunc, null, new IExiledEvent[]
            {
                EventProcessor.OnDying
            }, null, null)
            {
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, IExiledEvent e, AbilityTrigger trigger)
            {
                if (e is DyingEventArgs ev && ev.Player == player)
                {
                    ev.IsAllowed = false;
                    ev.Player.Health = ev.Player.MaxHealth;
                    var ca = ev.Player.ReferenceHub.playerEffectsController.GetEffect<CardiacArrest>();
                    ca.SetAttacker(ev.Attacker?.ReferenceHub);
                    ca.Intensity = 1;
                    ca.ServerChangeDuration(20f);
                    ev.Player.EnableEffect<Invisible>(3f);
                    return new AbilityOutput(true);
                }
                return new AbilityOutput(false);
            }

            private static bool AbilityCheck(ref AbilityInfo info, IExiledEvent e, Player player)
            {
                if (e is DyingEventArgs ev && ev.Player == player)
                {
                    return true;
                }
                return false;
            }
        }

        public sealed class Octane : OffensiveAbility
        {
            public Octane() : base("Octane", "失去10%生命（SCP为5%)，获得3级207效果10秒", 10, null, AbilityFunc, AbilityChecker, null, null, null)
            {
            }

            private static bool AbilityChecker(Player instance, out string response)
            {
                if (instance.Health <= 10f)
                {
                    response = "<=10HP时不可用";
                    return false;
                }
                response = "";
                return true;
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                float percent = player.IsScp ? 0.05f : 0.1f;
                float final = player.Health - player.MaxHealth * percent;
                player.Health = Math.Max(1f, final);
                player.EnableEffect<Scp207>(3, 10f);
                return new AbilityOutput(true);
            }
        }

        public sealed class EMP : OffensiveAbility
        {
            public EMP() :
                base("EMP", "使用后附近15m所有敌方技能全部失效15s", 60, null, AbilityFunc, null, null, null, null)
            {
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                foreach (Player p in Player.List.Where(x => Vector3.Distance(player.Position, x.Position) <= 15f &&
                !MethodExtensions.IsFriendly(player, x) && player != x))
                {
                    if (!AbilityManager.AbilityInvalid.ContainsKey(p))
                    {
                        AbilityManager.AbilityInvalid.Add(p, DateTime.Now.AddSeconds(15f));
                    }
                    else
                    {
                        AbilityManager.AbilityInvalid[p] = AbilityManager.AbilityInvalid[p].AddSeconds(15f);
                    }
                }
                return new AbilityOutput(true);
            }
        }

        public sealed class Blind : AttributeAbility
        {
            public Blind() :
                base("Blind", "让攻击者拥有短暂视觉模糊", AbilityFunc, false, new IExiledEvent[]
                {
                    EventProcessor.OnHurting
                }, null, null)
            {
            }

            private static bool AbilityFunc(ref AbilityInfo info, IExiledEvent e, Player player)
            {
                if (e is HurtingEventArgs ev && ev.DamageHandler.Base is PlayerStatsSystem.AttackerDamageHandler)
                {
                    ev.Attacker?.EnableEffect<Blinded>(3f);
                }
                return false;
            }
        }

        public sealed class Unloaded : PassiveAbility
        {
            public Unloaded() : base("Unloaded", "血量低于30（SCP为低于2/3)受到伤害时，卸下附近敌方8名玩家子弹并减速", 150, AbiltiyCheck, AbilityFunc, null, new IExiledEvent[]
            {
                EventProcessor.OnHurting
            }, null, null)
            {
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, IExiledEvent e, AbilityTrigger trigger)
            {
                if (e is HurtingEventArgs ev && ev.Player == player)
                {
                    int count = 0;
                    foreach (Player p in Player.List.Where(x => Vector3.Distance(player.Position, x.Position) <= 20f &&
                    !MethodExtensions.IsFriendly(player, x) && player != x))
                    {
                        if (p.CurrentItem?.Base is Firearm f)
                        {
                            f.AmmoManagerModule.ServerTryUnload();
                        }
                        p.EnableEffect<Disabled>(5f);
                        count++;
                        if (count >= 8)
                            break;
                    }
                    return new AbilityOutput(true);
                }
                return new AbilityOutput(false);
            }

            private static bool AbiltiyCheck(ref AbilityInfo info, IExiledEvent e, Player player)
            {
                if (e is HurtingEventArgs ev && ev.Player == player && ev.Player.Health <= (ev.Player.IsScp ? ev.Player.MaxHealth * 0.66f : 30f))
                {
                    return true;
                }
                return false;
            }
        }

        public sealed class Tempt : OffensiveAbility
        {
            public Tempt() : base("Tempt", "魅惑附近一名敌人，使其加入自己阵营", 30, null, AbilityFunc, null, null, null, null)
            {
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                var list = Player.List.Where(x => !x.IsScp && Vector3.Distance(player.Position, x.Position) <= 10f
                && player != x && !MethodExtensions.IsFriendly(player, x));
                if (list.Count() > 0)
                {
                    Player target = list.ElementAt(list.Count() - 1);
                    RoleTypeId targetRole = RoleTypeId.Tutorial;
                    switch (player.GetSide())
                    {
                        case SideType.Scp:
                            targetRole = RoleTypeId.Scp0492; break;
                        case SideType.Mtf:
                            targetRole = RoleTypeId.NtfPrivate; break;
                        case SideType.ChaosInsurgency:
                            targetRole = RoleTypeId.ChaosConscript; break;
                    }
                    target.EnableEffect<Ensnared>(5f);
                    player.EnableEffect<Ensnared>(5f);
                    Timing.CallDelayed(5.0f, delegate ()
                    {
                        if (target.IsAlive && player.IsAlive)
                        {
                            target.Role.Set(targetRole, RoleSpawnFlags.None);
                        }
                    });
                    return new AbilityOutput(true, "魅惑中");
                }
                return new AbilityOutput(false, "没有可以引诱的对象");
            }
        }

        public sealed class Revive : OffensiveAbility
        {
            public Revive() : base("Revive", "召唤3个保镖（获得100盾）为你作战", 180, null, AbilityFunc, AbilityCheck, null, null, null)
            {
            }

            private static bool AbilityCheck(Player instance, out string response)
            {
                if (Player.List.Count(x => !x.IsAlive) < 3)
                {
                    response = "旁观位人数小于3";
                    return false;
                }
                response = "";
                return true;
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                var list = Player.List.Where(x => !x.IsAlive);
                for (int i = 0; i < list.Count() && i < 3; i++)
                {
                    Player p = list.ElementAt(i);
                    RoleTypeId tar = RoleTypeId.Spectator;
                    switch (player.GetSide())
                    {
                        case SideType.Scp: tar = RoleTypeId.Scp0492; break;
                        case SideType.Mtf: tar = RoleTypeId.FacilityGuard; break;
                        case SideType.ChaosInsurgency: tar = RoleTypeId.ChaosConscript; break;
                    }
                    p.Role.Set(tar, RoleSpawnFlags.All);
                    Timing.CallDelayed(0.5f, delegate ()
                    {
                        p.Teleport(player.Position);
                    });
                    if (tar.IsHuman())
                    {
                        p.GetModule<AhpStat>().CurValue = 100f;
                    }
                    else
                    {
                        p.GetModule<HumeShieldStat>().CurValue = 100f;
                    }
                }
                return new AbilityOutput(true, "召唤！");
            }
        }

        public sealed class Candy : OffensiveAbility
        {
            public Candy()
                : base("Candy", "来颗糖，兄弟", 30, null, AbilityFunc, null, null, null, null)
            {
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                var candies = Enum.GetValues(typeof(InventorySystem.Items.Usables.Scp330.CandyKindID)).ToArray<InventorySystem.Items.Usables.Scp330.CandyKindID>().Where(x => x != InventorySystem.Items.Usables.Scp330.CandyKindID.None);
                player.TryAddCandy(candies.ElementAt(UnityEngine.Random.Range(0, candies.Count())));
                return new AbilityOutput(true, "Candy, bro.");
            }
        }

        public sealed class BoyNextDoor : OffensiveAbility
        {
            public BoyNextDoor() : base("BoyNextDoor", "Boy ♂ Next ♂ Door", 70, null, AbilityFunc, null, null, null, null)
            {
            }

            private static AbilityOutput AbilityFunc(Player player, ref AbilityInfo info, AbilityTrigger trigger)
            {
                Player target = API.IsPointingAt(player, 15f);
                if (target == null)
                    return new AbilityOutput(false, "需要瞄准一个目标");
                if (MethodExtensions.IsFriendly(player, target))
                    return new AbilityOutput(false, "此技能不能对着友方使用");
                AdminToyBase origin = null;
                foreach (var value in NetworkClient.prefabs.Values)
                {
                    if (value.TryGetComponent<AdminToyBase>(out var at) && at.CommandName == "TargetDBoy")
                    {
                        origin = at;
                        break;
                    }
                }
                if (origin == null)
                    return new AbilityOutput(false, "出现内部错误，请通知技术组");
                List<AdminToyBase> toys = ListPool<AdminToyBase>.Pool.Get();
                float offset = 0.6f;
                AdminToyBase toy1 = UnityEngine.Object.Instantiate(origin);
                toy1.transform.position = target.Position + new Vector3(1f, -1f, 0f) * offset;
                toy1.transform.rotation = Quaternion.AngleAxis(0f, Vector3.up);
                toy1.SpawnerFootprint = new Footprinting.Footprint(player.ReferenceHub);
                toys.Add(toy1);
                NetworkServer.Spawn(toy1.gameObject);
                AdminToyBase toy2 = UnityEngine.Object.Instantiate(origin);
                toy2.transform.position = target.Position + new Vector3(-1f, -1f, 0f) * offset;
                toy2.transform.rotation = Quaternion.AngleAxis(180f, Vector3.up);
                toy2.SpawnerFootprint = new Footprinting.Footprint(player.ReferenceHub);
                toys.Add(toy2);
                NetworkServer.Spawn(toy2.gameObject);
                AdminToyBase toy3 = UnityEngine.Object.Instantiate(origin);
                toy3.transform.position = target.Position + new Vector3(0f, -1f, 1f) * offset;
                toy3.transform.rotation = Quaternion.AngleAxis(270f, Vector3.up);
                toy3.SpawnerFootprint = new Footprinting.Footprint(player.ReferenceHub);
                toys.Add(toy3);
                NetworkServer.Spawn(toy3.gameObject);
                AdminToyBase toy4 = UnityEngine.Object.Instantiate(origin);
                toy4.transform.position = target.Position + new Vector3(0f, -1f, -1f) * offset;
                toy4.transform.rotation = Quaternion.AngleAxis(90f, Vector3.up);
                toy4.SpawnerFootprint = new Footprinting.Footprint(player.ReferenceHub);
                toys.Add(toy4);
                NetworkServer.Spawn(toy4.gameObject);
                target.EnableEffect<Flashed>(10f);
                Timing.CallDelayed(20f, delegate ()
                {
                    foreach (var x in toys)
                    {
                        if (x is ShootingTarget t)
                        {
                            t.ClearTarget();
                        }
                        UnityEngine.Object.Destroy(x.gameObject);
                    }
                });
                return new AbilityOutput(true, "Oh ♂ Yeah~");
            }
        }
    }
}